package com.analyse.stock.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;

import java.util.Properties;

@Configuration
@EnableScheduling
@EnableConfigurationProperties()
public class QuartzConfiguration {
    @Value("${spring.quartz.jdbc.driverClassName:com.mysql.cj.jdbc.Driver}")
    private String driverClassName;

    @Value("${spring.quartz.jdbc.url:jdbc:mysql://192.168.1.121:13306/stock_quartz?serverTimezone=Asia/Shanghai&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false}")
    private String url;

    @Value("${spring.quartz.jdbc.username:root}")
    private String username;

    @Value("${spring.quartz.jdbc.password:qsdi}")
    private String password;

    @Value("${spring.quartz.scheduler.instanceName}")
    private String instanceName;

    @Value("${spring.quartz.scheduler.jobStoreClass:org.quartz.impl.jdbcjobstore.JobStoreTX}")
    private String jobStoreClass;

    @Value("${spring.quartz.scheduler.isClustered:true}")
    private String isClustered;

    /**
     * 继承org.springframework.scheduling.quartz.SpringBeanJobFactory
     * 实现任务实例化方式
     */
    public static class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {

        private transient AutowireCapableBeanFactory beanFactory;

        @Override
        public void setApplicationContext(final ApplicationContext context) {
            beanFactory = context.getAutowireCapableBeanFactory();
        }

        /**
         * 将job实例交给spring ioc托管
         * 我们在job实例实现类内可以直接使用spring注入的调用被spring ioc管理的实例
         * @param bundle
         * @return
         * @throws Exception
         */
        @Override
        protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
            final Object job = super.createJobInstance(bundle);
            /**
             * 将job实例交付给spring ioc
             */
            beanFactory.autowireBean(job);
            return job;
        }
    }

    /**
     * 配置任务工厂实例
     * @param applicationContext spring上下文实例
     * @return
     */
    @Bean
    public JobFactory jobFactory(ApplicationContext applicationContext)
    {
        /**
         * 采用自定义任务工厂 整合spring实例来完成构建任务
         * see {@link AutowiringSpringBeanJobFactory}
         */
        AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
        jobFactory.setApplicationContext(applicationContext);
        return jobFactory;
    }

    public DruidDataSource jdbcDruidDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(url);
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setTestOnBorrow(false);
        dataSource.setTestOnReturn(false);
        dataSource.setTestWhileIdle(true);
        dataSource.setTimeBetweenEvictionRunsMillis(60000);
        dataSource.setPoolPreparedStatements(true);
        dataSource.setMinIdle(10);
        dataSource.setMaxActive(100);
        dataSource.setInitialSize(10);
        dataSource.setMaxWait(60000);
        return dataSource;
    }

    /**
     * 配置任务调度器
     * 使用项目数据源作为quartz数据源
     * @param jobFactory 自定义配置任务工厂
     * @return
     * @throws Exception
     */
    @Bean(destroyMethod = "destroy",autowire = Autowire.NO)
    public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) throws Exception {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setJobFactory(jobFactory);
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        schedulerFactoryBean.setStartupDelay(5);
        schedulerFactoryBean.setAutoStartup(true);
        if(Boolean.valueOf(isClustered)){
            schedulerFactoryBean.setDataSource(jdbcDruidDataSource());
        }
        schedulerFactoryBean.setApplicationContextSchedulerContextKey("applicationContext");
        Properties prop = new Properties();
        prop.put("org.quartz.scheduler.instanceName", instanceName);
        prop.put("org.quartz.scheduler.instanceId", "AUTO");
        prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        prop.put("org.quartz.threadPool.threadCount", "20");
        prop.put("org.quartz.threadPool.threadPriority", "5");
        prop.put("org.quartz.jobStore.class",jobStoreClass);
        if(Boolean.valueOf(isClustered)){
            prop.put("org.quartz.jobStore.isClustered", isClustered);
            prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
            prop.put("org.quartz.jobStore.lockHandler.class","org.quartz.impl.jdbcjobstore.StdRowLockSemaphore");
            prop.put("org.quartz.jobStore.clusterCheckinInterval", "1000");
            prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
            prop.put("org.quartz.jobStore.acquireTriggersWithinLock", "true");
            prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
            prop.put("org.quartz.jobStore.misfireThreshold", "5000");
        }
        schedulerFactoryBean.setQuartzProperties(prop);
        return schedulerFactoryBean;
    }

}